A Modem Driver is used to change, enhance, or extend the capabilities of the standard Modem menu, Procedure, and Phonebook modem handling routines.
Because the standard routines are hard-wired to Hayes compatibility standards, they would not be appropriate for non-Hayes compatible modems, PBX's or switchboards, or mainframe port selectors.
If a particular user would be directly connected via serial cable to a mini or mainframe, and would never be using a modem, it might be desirable to modify the "Initialize Modem" command to perform the function of logging into the system. Additionally, the "Hang Up Modem" command routine might be modified to perform network disconnection. A Modem Driver can modify any or all of the standard routines in the Modem menu, the Procedure command equivalents for these menu choices, and the Phonebook "Call" button.
If you were setting up a system for a novice, you might want to modify the "Dial Or Redial" menu choice to bypass the standard dialog box and dial a specific number or even perform an auto-logon Procedure.
Another issue is the fact that "Hayes compatible" has become an extremely muddy issue. Some modems I've tested could be termed "Hayes compatible" only as an act of charity. Others do offer a broad implementation of the "AT" command set, but go far beyond this with supersets of commands to take advantage of brand specific features. A Modem Driver has the capability to give additional handholding to "brain damaged" modems, or it could be used to append additional user-defined routines to the end of the Modem menu. These additional commands, if implemented, can also be called by a Procedure file.
You might want to add commands for such things as:
• Manipulating brand-specific features.
• Manipulating the "S" registers.
• Implementing additional modem routines, such as "Wait For A Call" or "Switch From Data To Voice" and "Switch From Voice To Data".
The capabilities of a White Knight 11 Modem Driver go far beyond those offered by any other telecommunications program. I think as you read forward in this document you'll appreciate the wide variety of opportunities available to you.
What Is A Modem Driver?
The essence of a Modem Driver is that it is nothing more than a Procedure File, which makes it extremely easy to create, modify, test, and debug. What we are doing is taking advantage of the NEST command to branch to the Modem Driver Procedure. Therefore, this is why I advise Procedure developers never to use the 6th level of Procedure nesting - this must be reserved for any Modem Driver that might be present. Take for instance the Procedure command DIAL. If a Modem Driver is present when this command is executed, the standard routines are not executed.
Instead, a special variable is set to inform the Modem Driver which routine has been requested, and then a NEST command is done to execute the Modem Driver. When the Modem Driver is finished processing the request, it quits with a NESTEND command, which continues execution in the calling Procedure with the command following the DIAL command. In this way, we accomplish complete transparency to the calling Procedure - a Procedure should never need to know if it will be "talking" to the standard routines or a Modem Driver. To be a "Well Behaved Modem Driver" you need to observe the following rules:
• Always return the same flag settings as the normal routines would.
• Never change any variables (string or numeric). Two Procedure commands are given to set up and restore the variables that will always be used by Modem Drivers. If you modify any others, it would be wise to use SAVEVAR and LOADVAR Procedure commands at the beginning and end of your Modem Driver routines to make sure they are restored to the same condition upon exiting your Modem Driver as they were when the Modem Driver was entered.
• Give your user some feedback as to what the Modem Driver is doing if it's not obvious. Do not do anything, however, that would "freeze up" an unattended Procedure file. Take advantage of the User Defined Window Procedure commands when appropriate and perhaps even RCMD modules!
• When a Modem Driver is installed, White Knight assumes that the Modem Driver replaces all of the standard modem handling routines. Therefore, your Modem Driver must implement all of the routines, and not just add or modify one or two. However, there are hooks provided to the standard routines so this is very easy to accomplish.
• If your Modem Driver is for a non-Hayes compatible modem, please document how the user should format their dialing strings (rather than preceding them with "ATDT", for instance) in their Procedure files and Phonebook entries.
How To Write A Modem Driver
"Sample Modem Driver.src" is provided at the end of this document to demonstrate how a Modem Driver is written. It implements all of the standard routines, and adds one additional routine. Be sure and study this Procedure in detail before attempting to write your own Modem Driver.
Whenever a menu choice, Phonebook "Call" button or Procedure command is executed that would normally call upon the standard modem handling routines, your Modem Driver is called instead. Modem drivers use 3 variables: A%, which tells the Modem Driver which routine to execute, and D$ and H$ which is used by the dialing/redialing routines. However, remember that you absolutely never want to change any variables, since the Procedure that called our Modem Driver (unknowingly) might be using any or all of those variables for something else. Therefore a special command used only by Modem Drivers is executed at the beginning and end of the Modem Driver to insure that these variables are not mucked up.
SETUP or_or_off_exp
Description: The command SETUP ON swaps the current values of the variables A%, D$, and H$ to a holding area and swaps in the values expected by a Modem Driver. SETUP OFF returns the values of these variables to what they were before the Modem Driver was executed. SETUP ON should always be the absolute first command in your Modem Driver. SETUP OFF will always be the second to last command in your Modem
Driver (before NESTEND).
If you intend to use any other variables, you should use SAVEVAR before and LOADVAR after any routines that alter them. You'll see where I do exactly this in a couple of places in "Sample Modem Driver.src"
After the SETUP ON command, the numeric variable A% is set to a number from 0 to 31 that tells your Modem Driver which routine should be executed. Therefore, a Modem Driver will always begin with a batch of TEST and IF YES JUMPTO commands to dispatch to the proper routine. Here are what the numbers in A% correspond to:
Routine 0: Initialize Modem Driver
This routine is always called only once when the Modem Driver is first installed. You can do any initialization your Modem Driver requires in this routine. This routine is also where you'll install any user routines you wish to implement. You may implement up to 10 user routines, numbered 0 through 9 in that order, but you should always implement them in ascending order without gaps so they'll show up in the proper position in the Modem menu. In other words, don't implement user routine #1 unless you have implemented user routine #0 first. A special Procedure command is used to append a user routine choice to the end of the Modem menu:
USERMITEM str_exp
Description: This command appends the text in str_exp to the end of the Modem menu. Double command-key menu choice equivalents are automatically supported for any implemented user routines. These will be the letter M followed by a number from zero to nine depending on the user routine number. You must execute the USERMITEM commands in the order of the lowest user routine (always #0) to the highest. White Knight will always assume that the first USERMITEM command encountered is for user routine number zero, the second USERMITEM command encountered is for user routine number one, and so on. Because the number zero might be confused with the letter O in the menu, be sure to document to your users that this is in fact a zero.
You might wish to enable or disable a particular user routine menu item at a particular time. The following Procedure command will do this:
HILITEMITEM num_exp,on_or_off
Description: This command will enable or disable the user routine menu item associated with num_exp, which should be in the range 0 through 9. If on_or_off is ON, the menu item is enabled. If on_or_off is OFF, the menu item is disabled.
Routine 1: Initialize The Modem (From A Menu Choice)
In this routine, you should attempt to communicate with the modem and verify that it will accept further commands.
Routine 2: Dial Or Redial (From A Menu Choice)
In this routine, you should get the phone number to dial (most likely from a Dialog box), and then do the actual dialing or redialing.
Routine 3: Turn On Auto-Answer (From A Menu Choice)
In this routine, you should instruct the modem to automatically answer
any future incoming calls.
Routine 4: Turn Off Auto-Answer (From A Menu Choice)
In this routine, you should instruct the modem to no longer answer
any future incoming calls.
Routine 5: Hang Up Modem (From A Menu Choice)
In this routine, you should instruct the modem to disconnect the
current call.
Routine 6: Execute User Routine #0 (From A Menu Choice)
Execute your user routine #0.
Routine 7: Execute User Routine #1 (From A Menu Choice)
Execute your user routine #1.
Routine 8: Execute User Routine #2 (From A Menu Choice)
Execute your user routine #2.
Routine 9: Execute User Routine #3 (From A Menu Choice)
Execute your user routine #3.
Routine 10: Execute User Routine #4 (From A Menu Choice)
Execute your user routine #4.
Routine 11: Execute User Routine #5 (From A Menu Choice)
Execute your user routine #5.
Routine 12: Execute User Routine #6 (From A Menu Choice)
Execute your user routine #6.
Routine 13: Execute User Routine #7 (From A Menu Choice)
Execute your user routine #7.
Routine 14: Execute User Routine #8 (From A Menu Choice)
Execute your user routine #8.
Routine 15: Execute User Routine #9 (From A Menu Choice)
Execute your user routine #9.
Routine 16: Initialize The Modem (From A Procedure)
In this routine, you should attempt to communicate with the modem and verify that it will accept further commands. If the initialization was successful, you should set the ERROR flag to NO ERROR. If it failed, the ERROR flag should be set to ERROR.
Routine 17: Dial (From A Procedure)
In this routine, you should dial the number contained in the string variable H$. H$ comes from the Procedure DIAL command, and you should expect that it will be in the form of a Hayes compatible modem dialing command (example "ATDT 555-1212") unless you've documented to your user to do otherwise. If the dialing was cancelled by the user, you should set the YES/NO flag to YES and the ERROR flag to NO ERROR. If the dialing failed for some reason, you should set the YES/NO flag to NO and the ERROR flag to ERROR. If a connection is established, you should set the YES/NO flag to YES and the ERROR flag to NO ERROR. If you couldn't communicate with the modem, you should set the YES/NO flag to YES and the ERROR flag to ERROR.
Routine 18: Redial (From A Procedure Or Phonebook)
In this routine, you should redial the number contained in the string variable H$. H$ comes from the Procedure REDIAL command or the Phonebook number to dial, and you should expect that it will be in the form of a Hayes compatible modem dialing command (example: "ATDT 555-1212"). If the redial was cancelled by the user, you should set the YES/NO flag to YES and the ERROR flag to NO ERROR. If the redialing failed for some reason, you should set the YES/NO flag to NO and the ERROR flag to ERROR. If a connection is established, you should set the YES/NO flag to YES and the ERROR flag to NO ERROR. If you couldn't communicate with the modem, you should set the YES/NO flag to YES and the ERROR flag to ERROR.
Routine 19: Turn On Auto-Answer (From A Procedure)
In this routine, you should instruct the modem to automatically answer
any incoming calls. If this routine was successful, the ERROR flag should be set to NO ERROR. If it failed, the ERROR flag should be set to ERROR.
Routine 20: Turn Off Auto-Answer (From A Procedure)
In this routine, you should instruct the modem to no longer answer
any incoming calls. If this routine was successful, the ERROR flag should be set to NO ERROR. If it failed, the ERROR flag should be set to ERROR.
Routine 21: Hang Up Modem (From A Procedure)
In this routine, you should instruct the modem to disconnect the
current call. If this routine was successful, the ERROR flag should be set to NO ERROR. If it failed, the ERROR flag should be set to ERROR.
Routine 22: Execute User Routine #0 (From A Procedure)
Execute your user routine #0. Flag settings are entirely up to you, but should be documented.
Routine 23: Execute User Routine #1 (From A Procedure)
Execute your user routine #1. Flag settings are entirely up to you, but should be documented.
Routine 24: Execute User Routine #2 (From A Procedure)
Execute your user routine #2. Flag settings are entirely up to you, but should be documented.
Routine 25: Execute User Routine #3 (From A Procedure)
Execute your user routine #3. Flag settings are entirely up to you, but should be documented.
Routine 26: Execute User Routine #4 (From A Procedure)
Execute your user routine #4. Flag settings are entirely up to you, but should be documented.
Routine 27: Execute User Routine #5 (From A Procedure)
Execute your user routine #5. Flag settings are entirely up to you, but should be documented.
Routine 28: Execute User Routine #6 (From A Procedure)
Execute your user routine #6. Flag settings are entirely up to you, but should be documented.
Routine 29: Execute User Routine #7 (From A Procedure)
Execute your user routine #7. Flag settings are entirely up to you, but should be documented.
Routine 30: Execute User Routine #8 (From A Procedure)
Execute your user routine #8. Flag settings are entirely up to you, but should be documented.
Routine 31: Execute User Routine #9 (From A Procedure)
Execute your user routine #9. Flag settings are entirely up to you, but should be documented.
Why The Differentiation?
You'll notice that the above routines differentiate between those called from a menu choice and those called from a Procedure (or Phonebook "Call" button). There are two good reasons for this.
• If they were called from a Procedure, you would never want to display a dialog box that would freeze up an unattended Procedure. In this case, always use the same flag settings as the built in routines to tell the Procedure what happened and rely on the Procedure developer to trap for and react properly to these flag settings. Conversely, if they were called from a menu choice, you would want to use some sort of dialog box explaining the result of the operation (especially in the case of failure).
• The dial and redial routines are different for menu choices and Procedure/Phonebook calls. From a menu choice, you need to get at least the phone number to dial from a dialog box. From a Procedure or Phonebook "Call" button, the dialing command is already supplied
You don't have to have duplicate routines in your Modem Driver for instances when it doesn't matter how the routine was called (from a menu or a Procedure/Phonebook), just trap for both routine numbers (from a menu or from a Procedure) and branch to the same routine. You can set the flags any way you like, or not at all, for routines called from a menu.
How To Set The Flags
You might be wondering just to how accomplish a certain flag setting. The following code snippits demonstrate how to do this.
To set the YES/NO flag to YES:
LET EQUAL A%,1
TEST A% = 1
To set the YES/NO flag to NO:
LET EQUAL A%,1
TEST A% = 2
To set the ERROR flag to ERROR:
LET EQUAL A%,1
DIVIDE A%,0
To set the ERROR flag to NO ERROR:
LET EQUAL A%,1
DIVIDE A%,1
Hooking Into The Standard Routines
As mentioned, your Modem Driver must implement all of the standard modem handling routines. However, since you might not wish to modify the built in routines, I have provided hooks for your modem driver to execute the various built in routines. "Sample Modem Driver.src" shows how these hooks are used for all of the standard routines, called from both menu choices and Procedure/Phonebook calls.
A special Procedure command used only by Modem Drivers is used to execute a built in modem handling routine:
MROUTINE num_exp
Description: This command executes the built in modem handling routine specified in num_exp. These routines are:
Built In Routine 0: Initialize The Modem
This routine will do the built in modem initialization routine. If the initialization was successful, the ERROR flag will be set to NO ERROR, otherwise, the ERROR flag is set to ERROR. This routine knows whether or not a Procedure is executing "underneath" your Modem Driver, so it can be called either from a menu choice or a Procedure command.
Built In Routine 1: Do The Dial/Redial Dialog Box
This routine will execute the built in routine for displaying the "Dial Or Redial" dialog box, therefore it's only appropriate for use from a menu choice. The YES/NO flag is set to NO if the user clicked the "Cancel" button, otherwise it's set to YES. The digits to dial are returned in D$, and the Hayes compatible dialing command is returned in H$. There are a number of GETPARAM commands you might also want to use to get the rest of the settings from this dialog box.
GETPARAM 474,A% would return in A% a zero if the user wishes to redial, or a one if the user wishes to dial once.
GETPARAM 473,A% would return in A% a zero if the user wishes to use touch-tone dialing or one if the user wishes to use pulse dialing.
GETPARAM 475,A% would return in A% the number of seconds the user wishes to wait for a connection after dialing before giving up.
GETPARAM 476,A% would return in A% the modem command inter-character delay in 60'ths of a second.
GETPARAM 505,A% would return in A% the redialing attempt limit.
Built In Routine 2: Perform Dialing/Redialing From Menu
This command performs the built in dialing or redialing (in accordance with all the GETPARAM settings described under "Built In Routine 1") above. You should pass a Hayes compatible dialing command in H$ and the digits to be dialed in D$ (the digits are merely displayed in the dialog box during dialing and are not used). This routine does use dialog boxes depending on success or failure of the routine, so it should only be called from a menu choice. If the dial or redial was cancelled by the user, the YES/NO flag is set to YES and the ERROR flag is set to NO ERROR. If the dial or redial failed for some reason, the YES/NO flag is set to NO and the ERROR flag is set to ERROR. If a connection is established, the YES/NO flag is set to YES and the ERROR flag is set to NO ERROR.
Built In Routine 3: Turn On Auto-Answer
This routine executes the built in routine to instruct the modem to auto-answer incoming calls. If the routine was successful, the ERROR flag is set to NO ERROR, otherwise, the ERROR flag is set to ERROR. This routine is smart enough to know whether there's a Procedure running "underneath" your Modem Driver, so it can be called from either a menu choice or a Procedure/Phonebook call.
Built In Routine 4: Turn Off Auto-Answer
This routine executes the built in routine to instruct the modem to no longer auto-answer incoming calls. If the routine was successful, the ERROR flag is set to NO ERROR, otherwise, the ERROR flag is set to ERROR. This routine is smart enough to know whether there's a Procedure running "underneath" your Modem Driver, so it can be called from either a menu choice or a Procedure/Phonebook call.
Built In Routine 5: Hang Up
This routine executes the built in routine to instruct the modem to disconnect the current call. If the routine was successful, the ERROR flag is set to NO ERROR, otherwise, the ERROR flag is set to ERROR. This routine is smart enough to know whether there's a Procedure running "underneath" your Modem Driver, so it can be called from either a menu choice or a Procedure/Phonebook call.
Built In Routine 6: Dial Or Redial From Procedure/Phonebook
This built in routine functions exactly as that described under "Built In Routine 2" except that no dialog boxes are used to denote the success or failure of the connection. Therefore, this routine is to be used with Procedure or Phonebook "Call" button calls.
Procedure Commands That Deal With Modem Drivers
There are a number of Procedure commands that are used to affect the performance of a Modem Driver (however, these commands should not be used inside of a Modem Driver):
MDRIVER on_or_off
Description: MDRIVER ON tells White Knight to use the currently selected Modem Driver. MDRIVER OFF tells White Knight to use the standard built in routines.
USEDRIVER str_exp
Description: This command instructs White Knight which Modem Driver to use. It should be preceded by a MDRIVER ON command.
MFUNCTION num_exp
Description: This command is used by a Procedure to call a user routine in a Modem Driver. Num_exp should be in the range zero to 9 corresponding to the user routine to execute. This will generate a routine number request in the range 22 through 31 to the Modem Driver. If the user routine has not been implemented (no corresponding USERMITEM has been done by the Modem Driver's installation routine), the Procedure command will do nothing. Of course, it's up to you to document what your routines do so that Procedure developers will be able to call the appropriate routine in your Modem Driver.
Some Final Thoughts
White Knight 11 is the first version to support built in modem handling routines. I resisted implementing these in previous versions due to the fact that I felt I couldn't do a thorough job of taking advantage of the capabilities of the different modems without being brand specific. My final decision was to add support for at least the lowest common denominator level, and then provide the capability for future enhancement. I hope you'll take the ball and run with it, because I see some excellent possibilities available with Modem Drivers. As always, your feedback and suggestions are greatly appreciated.
Procedure File Source Code For "Sample Modem Driver.src"
(Since we're a guest in someone else's house and don't want to muck with)
(any variables, we'll use a SETUP ON command at the beginning to set up the)
(value of A%, D$, and H$ properly, and a single exit point for the SETUP)
(OFF command to restore them back to what they were. If we didn't do this,)
(a Procedure author (who doesn't know we exist) might be pushed to the)
(point of suicide (or homicide - gulp) over why these variables seem to)
(change spontaneously. If you play with any other variables, you oughtta) (consider using SAVEVAR and LOADVAR commands at beginning and end.)
SETUP ON
(The following batch of lines parses out what I'm supposed to do and)
(branches to the appropriate routine. I want this to happen as quickly)
(as possible so I'll start with a LOCK ON. Each routine will begin with)
(a LOCK OFF.)
LOCK ON
TEST A% = 0
IF YES JUMPTO INIT_DRIVER
TEST A% = 1
IF YES JUMPTO INIT_MODEM
TEST A% = 2
IF YES JUMPTO DIAL_REDIAL
TEST A% = 3
IF YES JUMPTO AUTO_ANSWER_ON
TEST A% = 4
IF YES JUMPTO AUTO_ANSWER_OFF
TEST A% = 5
IF YES JUMPTO HANG_UP
TEST A% = 6
IF YES JUMPTO USER_0
TEST A% = 16
IF YES JUMPTO INIT_MODEM
TEST A% = 17
IF YES JUMPTO PROC_DIAL
TEST A% = 18
IF YES JUMPTO PROC_REDIAL
TEST A% = 19
IF YES JUMPTO AUTO_ANSWER_ON
TEST A% = 20
IF YES JUMPTO AUTO_ANSWER_OFF
TEST A% = 21
IF YES JUMPTO HANG_UP
TEST A% = 22
IF YES JUMPTO USER_0
(Although there's a failsafe routine later for unimplemented routines)
(called from a menu, I wouldn't want to put up a dialog box for)
(unimplemented routines called from a Procedure since the Procedure)
(might be running unattended and I don't want to freeze it.)
TEST A% >= 22
IF YES JUMPTO EXIT
(I don't recognize any further codes, if I fall through to here, I for)
(some reason have a menu choice for a function that doesn't exist. At any)
(rate, try to exit gracefully - I'm going to muck up X$, so I'll save the)
(contents of X$ through SAVEVAR and LOADVAR.)
LOCK OFF
SAVEVAR Modem Driver Scratch
COPYINTO X$,Function not implemented in this modem driver.
QUERY5
LOADVAR Modem Driver Scratch
(Get rid of the scratch file now)
DELETE Modem Driver Scratch
JUMPTO EXIT
(The only thing we need to install is our single user routine menu text.)
(However, I don't want installation interrupted so I'll turn on the watch)
(cursor and let the EXIT routine take care of the LOCK OFF.)
:INIT_DRIVER
WATCH ON
USERMITEM Wait For Call
JUMPTO EXIT
(Initialize the modem from a menu choice or Procedure INITMODEM command)
:INIT_MODEM
LOCK OFF
MROUTINE 0
JUMPTO EXIT
(First, do the Dial/Redial dialog box. If they OK'd it, perform)
(the dial or redial. This is done from a menu choice.)
:DIAL_REDIAL
LOCK OFF
MROUTINE 1
(Don't want to dial if they cancelled)
IF NO JUMPTO EXIT
(Don't want to dial if no numbers)
EMPTY D$
IF YES JUMPTO EXIT
MROUTINE 2
JUMPTO EXIT
(Turn on Auto-Answer from a menu choice or Procedure)
:AUTO_ANSWER_ON
LOCK OFF
MROUTINE 3
JUMPTO EXIT
(Turn off Auto-Answer from a menu choice or Procedure)
:AUTO_ANSWER_OFF
LOCK OFF
MROUTINE 4
JUMPTO EXIT
(Hang up from a menu choice or Procedure)
:HANG_UP
LOCK OFF
MROUTINE 5
JUMPTO EXIT
(Dial the number in H$ from the DIAL Procedure command.)
:PROC_DIAL
LOCK OFF
PUTPARAM 474,1
MROUTINE 6
JUMPTO EXIT
(Redial the number in H$ from the REDIAL Procedure command or the)
(Phonebook "Call" button.)
:PROC_REDIAL
LOCK OFF
PUTPARAM 474,0
MROUTINE 6
JUMPTO EXIT
(This is our additional user routine. It will wait up to 60 seconds)
(for a call. When it sees the "RING" message from the modem, it will)
(force the phone offhook and attempt to connect. If no call comes through)
(the YES/NO flag is set to NO. Otherwise, it's set to YES. If we wanted)
(to call this routine from a Procedure, we would do a "MFUNCTION 0")
(command.)
:USER_0
LOCK OFF
(First, init the modem to make sure I get any RING messages at the proper)
(baud rate.)
MROUTINE 0
(Don't do it if the initialization failed!)
IF ERROR JUMPTO EXIT
(SAVEVAR and LOADVAR do not save/load the @ variables, so there's no)
(reason to try and protect them. However, since this is a special)
(routine, it wouldn't be called with an "MFUNCTION 0" command unless)
(someone knew about it, which means I would simply document that this)
(routine would destroy the contents of the @ variables.)
ERASE @1
ERASE @2
COPYINTO @0,Now waiting up to 60 seconds for an incoming call.
@ ON
PANICAFTER 60
ONPANIC JUMPTO BAILOUT
PROMPT RING
PAUSE 10
TYPE ATA^M
(Set Yes/No flag to YES)
LET EQUAL A%,1
TEST A% = 1
@ OFF
JUMPTO EXIT
:BAILOUT
(Set Yes/No flag to NO)
LET EQUAL A%,1
TEST A% = 2
@ OFF
JUMPTO EXIT
:EXIT
(This is our modem driver's single exit point, where we'll use the)
(SETUP OFF command to restore the values of A%, D$, and H$ to what they)
(were before the modem driver was entered. Since the modem driver is)
(always called as a NEST'ed Procedure, we exit with a NESTEND command.)